﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NFramework;

namespace NFramework.UnitTests.DataAccessTests
{
    /// <summary>
    /// Summary description for FrameworkContextTests
    /// </summary>
    [TestClass]
    public class FrameworkContextTests : Global
    {
        [TestMethod]
        public void CurrentIsNeverNull()
        {
            Assert.IsNotNull(FrameworkContext.Current);
            FrameworkContext.Pop();
            Assert.IsNotNull(FrameworkContext.Current);
            FrameworkContext.PopAll();
            Assert.IsNotNull(FrameworkContext.Current);
        }

        [TestMethod]
        public void PopNegativeDoesNothing()
        {
            FrameworkContext current = FrameworkContext.Current;
            FrameworkContext.Pop(-1);
            Assert.AreSame(current, FrameworkContext.Current);
        }

        [TestMethod]
        public void PopZeroDoesNothing()
        {
            FrameworkContext current = FrameworkContext.Current;
            FrameworkContext.Pop(0);
            Assert.AreSame(current, FrameworkContext.Current);
        }

        [TestMethod]
        public void PopAllLeavesLastValidValue()
        {
            FrameworkContext.PopAll();
            FrameworkContext current = FrameworkContext.Current;
            FrameworkContext.Pop();
            Assert.AreSame(current, FrameworkContext.Current);
            FrameworkContext.PopAll();
            Assert.AreSame(current, FrameworkContext.Current);
        }

        [TestMethod]
        public void PopMaxShortCircuits()
        {
            FrameworkContext.PopAll();
            FrameworkContext current = FrameworkContext.Current;
            FrameworkContext.Pop(int.MaxValue);
            Assert.AreSame(current, FrameworkContext.Current);
        }

        [TestMethod]
        public void Pop10WorksProperly()
        {
            for (int i = 0; i < 11; i++)
            {
                MockContext.Push(i);
            }
            MockContext.Pop(10);
            Assert.AreEqual(MockContext.Current.Value, 0);
        }

        [TestMethod]
        public void NestedUsingBlockWorksWithDispose()
        {
            using (MockContext.Push(1))
            {
                Assert.AreEqual(MockContext.Current.Value, 1);
                using (MockContext.Push(2))
                {
                    Assert.AreEqual(MockContext.Current.Value, 2);
                    using (MockContext.Push(3))
                    {
                        Assert.AreEqual(MockContext.Current.Value, 3);
                    }
                    Assert.AreEqual(MockContext.Current.Value, 2);
                }
                Assert.AreEqual(MockContext.Current.Value, 1);
            }
            Assert.IsInstanceOfType(FrameworkContext.Current, typeof(FrameworkContext));
        }

        [TestMethod]
        public void NestedUsingBlockWorksWithDisposeOutOfOrder()
        {
            using (MockContext.Push(1))
            {
                Assert.AreEqual(MockContext.Current.Value, 1);
                using (MockContext.Push(2))
                {
                    Assert.AreEqual(MockContext.Current.Value, 2);
                    MockContext.Push(3);
                    Assert.AreEqual(MockContext.Current.Value, 3);
                }
                Assert.AreEqual(MockContext.Current.Value, 3);
            }
            Assert.AreEqual(MockContext.Current.Value, 3);
            MockContext.Pop();
            Assert.IsInstanceOfType(FrameworkContext.Current, typeof(FrameworkContext));
        }

        [TestMethod]
        public void DisposeOutOfOrderWorksFine()
        {
            MockContext m1 = MockContext.Push(1);
            MockContext m2 = MockContext.Push(2);
            MockContext m3 = MockContext.Push(3);
            Assert.AreEqual(MockContext.Current.Value, 3);
            MockContext m4 = MockContext.Push(4);
            m1.Dispose();
            Assert.AreEqual(MockContext.Current.Value, 4);
            m3.Dispose();
            m4.Dispose();
            Assert.AreEqual(MockContext.Current.Value, 2);
            m2.Dispose();
            Assert.IsInstanceOfType(FrameworkContext.Current, typeof(FrameworkContext));
        }

        [TestMethod]
        public void DisposingTwiceIsOk()
        {
            MockContext m1 = MockContext.Push(1);
            MockContext m2 = MockContext.Push(2);
            Assert.AreEqual(MockContext.Current.Value, 2);
            m1.Dispose();
            Assert.AreEqual(MockContext.Current.Value, 2);
            m1.Dispose();
            Assert.AreEqual(MockContext.Current.Value, 2);
            m1.Dispose();
            Assert.AreEqual(MockContext.Current.Value, 2);
            MockContext.Pop();
            Assert.IsInstanceOfType(FrameworkContext.Current, typeof(FrameworkContext));
        }
    }

    class MockContext : FrameworkContext
    {
        protected MockContext() { _key = Guid.NewGuid(); }

        public Guid Key
        {
            get { return _key; }
        }
        private Guid _key;

        public int Value
        {
            get { return _value; }
        }
        private int _value;

        public static MockContext Push() { return Push(0); }
        public static MockContext Push(int value)
        {
            var context = new MockContext();
            context._value = value;
            return FrameworkContext.Push<MockContext>(context);
        }

        public static new MockContext Current
        {
            get
            {
                return GetCurrent<MockContext>();
            }
        }
    }
}
